﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Reflection;
using System.Windows.Resources;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using exsportance.Silverlight.Common.Utils;

namespace exsportance.Silverlight.Common.Helper
{
    /// <summary>
    /// Helper for Instanziating Classes form downloaded assemblies and Xaps
    /// </summary>
    public class AssemblyHelper
    {
        #region const
        private const string AppManifestFileName = "AppManifest.xaml";
        private const string AssemblySourceAttributeName = "Source";
        private const string ApplicationBinaryContentTypeName = "application/binary";
        #endregion

        #region public methods
        /// <summary>
        /// Load a element from a Xap stream
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xapStream"></param>
        /// <param name="assemblyName"></param>
        /// <param name="className">Fullqualified class name</param>
        /// <returns></returns>
        public static T LoadElementFromXap<T>(Stream xapStream, string assemblyName, string className) where T : UIElement
        {
            T element = default(T);
            if (xapStream != null)
            {
                string applicationManifest = ReadApplicationManifest(xapStream);

                XElement deploymentElement = XDocument.Parse(applicationManifest).Root;
                IEnumerable<XElement> deploymentParts = from assemblyParts in deploymentElement.Elements().Elements()
                                                        select assemblyParts;

                Assembly assembly = LoadAssemblies(xapStream, assemblyName, deploymentParts);

                element = assembly.CreateInstance(className) as T;
            }
            return element;
        }

        /// <summary>
        /// Load an element from a assembly stream
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assemblyStream"></param>
        /// <param name="className">Fullqualified class name</param>
        /// <returns></returns>
        public static T LoadElementFromAssembly<T>(Stream assemblyStream, string className) where T : UIElement
        {
            AssemblyPart assemblyPart = new AssemblyPart();

            Assembly assembly = assemblyPart.Load(assemblyStream);

            T element = assembly.CreateInstance(className) as T;
            return element;
        }
        #endregion

        #region private methods
        private static Assembly LoadAssemblies(Stream xapStream, string assemblyName, IEnumerable<XElement> deploymentParts)
        {
            Assembly assembly = null;
            foreach (XElement assemblyPartElement in deploymentParts)
            {
                string name = assemblyPartElement.Attribute(AssemblySourceAttributeName).Value;

                AssemblyPart assemblyPart = new AssemblyPart();

                StreamResourceInfo assemblyResourceInfo = new StreamResourceInfo(xapStream, ApplicationBinaryContentTypeName);
                Uri assemblyUri = new Uri(name, UriKind.Relative);
                StreamResourceInfo streamInfo = Application.GetResourceStream(assemblyResourceInfo, assemblyUri);

                if (name == assemblyName)
                {
                    assembly = assemblyPart.Load(streamInfo.Stream);
                }
                else
                {
                    assemblyPart.Load(streamInfo.Stream);
                }
            }
            return assembly;
        }

        private static string ReadApplicationManifest(Stream xapStream)
        {
            StreamResourceInfo manifestResourceInfo = new StreamResourceInfo(xapStream, null);

            StreamResourceInfo applicationResourceStream = Application.GetResourceStream(manifestResourceInfo, new Uri(AppManifestFileName, UriKind.Relative));
            StreamReader streamReader = new StreamReader(applicationResourceStream.Stream);
            return streamReader.ReadToEnd();
        }
        #endregion
    }


}
